#include "header.h"
#include "util.h"
#include "unicode.h"

int power(int base, int exp)
{
  int product = 1;
  for(int i = exp; i > 0; i--)
    product *= base;
  return product;
}

static char_t symbols[] = "{}[]()!'£$%^&*-+=:;@~#<>,.?/\\|_\"`";

char_t is_bracket(char_t ch, int quote, int *is_start) {
    switch (ch) {
        case '[': {
          if(is_start != NULL) {
            *is_start = TRUE;
          }
          return ']';
        }
        case '(': {
          if(is_start != NULL) {
            *is_start = TRUE;
          }
          return ')';
        }
        case '{': {
          if(is_start != NULL) {
            *is_start = TRUE;
          }
          return '}';
        }
        case '<': {
          if(is_start != NULL) {
            *is_start = TRUE;
          }
          return '>';
        }
        case ']': return '[';
        case ')': return '(';
        case '}': return '{';
        case '>': return '<';
        case '"': return quote ? '"' : 0;
        case '\'': return quote ? '\'' : 0;
        case '`': return quote ? '`' : 0;
        default: return 0;
    }
    return 0;
}

int is_symbol(char_t c)
{
  register char_t *p = symbols;

  for (p = symbols; *p != '\0'; p++)
    if (*p == c) return 1;
  return 0;
}

// is_symbol ignore
int is_symboli(char_t c, char_t ignore)
{
  register char_t *p = symbols;

  for (p = symbols; *p != '\0'; p++)
    if (*p == c && *p != ignore) return 1;
  return 0;
}

// is_symbol ignore string
int is_symbolis(char_t c, const char *ignore)
{
  register char_t *p = symbols;
  int found = FALSE;

  for (p = symbols; *p != '\0'; p++)
    if (*p == c) {
      for(int i = 0; ignore[i] != '\0'; i++) {
        if(ignore[i] == c) {
          found = TRUE;
          break;
        }
      }
      return !found;
    }
  return 0;
}

void replace_all(char * str, char oldChar, char newChar)
{
  int i = 0;
  /* Run till end of string */
  while(str[i] != '\0')
  {
    /* If occurrence of character is found */
    if(str[i] == oldChar) {
      str[i] = newChar;
    }
    i++;
  }
}

void cleanup_path(char *path, char *output)
{
  char *dir, final_path[PATH_MAX+1] = "\0";
  const char *list_dirs[20];
  int i = 0;

  final_path[0] = '/';
  dir = strtok(path, "/");

  while( dir != NULL ) {
    if(dir[0] == '.' && dir[1] == '.') {
      i--;
    } else {
      list_dirs[i] = dir;
      i++;
    }
    dir = strtok(NULL, "/");
  }
  for(int z = 0 ; i > z; z++) {
    strcat(final_path, list_dirs[z]);
    if(z != i-1)
      strcat(final_path, "/");
  }
  final_path[PATH_MAX] = '\0';
  strcpy(output, final_path);
  return;
}

int is_quote(buffer_t *bp, char_t c)
{
  int as = TRUE, ab = TRUE;
  if(bp != NULL && bp->b_mode != NULL) {
    if(!bp->b_mode->sqas) {
      as = FALSE;
    }
    if(!bp->b_mode->bqas) {
      ab = FALSE;
    }
  }
  return (c == '\'' && as) || c == '"' || (c == '`' && ab);
}

point_t find_matching_bracket(buffer_t *bp, window_t *wp, int dir, int isrender)
{
  char_t *p, z, op, *tp;
  point_t cp = bp->b_point;
  int depth = 0, newlines = 0, instring = FALSE,
      /* only search for matches that are on the current page
         wp doesn't update until after render, so we can't use w_row and
         should use bp's b_row instead
      */
      lun = dir == -1 ? wp->w_rows - (bp->b_row - wp->w_rows) : wp->w_rows - bp->b_row;
  int isquote = FALSE, skip = FALSE;

  p = ptr(bp, cp);
  op = *p;
  isquote = is_quote(bp, *p);
  if((z = is_bracket(*p, TRUE, NULL)) == 0) {
    // TODO: jump over whitespace to get to bracket
    return -1;
  }
  if(dir == -1) {
    cp--;
    while ((*(p = ptr(bp, cp)) != z || depth > 0 || skip || instring) && cp >= 0) {
      if(*p == '\n')
        newlines++;
      /* Skip over stuff like '}' or '(' */
      if(!skip && is_quote(bp, *p)) {
        if(instring) {
          if(*p == *tp)
            instring = !instring;
        } else {
          int ii = cp-1, found = FALSE;
          tp = ptr(bp, ii);
          for(; ((bp->b_mode != NULL && !bp->b_mode->bmls) || *tp != '\n') && ii >= 0; ii--) {
            tp = ptr(bp, ii);
            if(*tp == *p) {
              found = TRUE;
              break;
            }
            if(ii >= 2 && *(tp = ptr(bp, ii-2)) == '\\')
              ii--;
          }
          if(found)
            instring = !instring;
        }
      }
      if(*p == op && !skip && !instring) {
        depth++;
      } else if(*p == z  && !instring) {
        depth--;
      }
      skip = FALSE;
      /* Imagine this case: "this is \"a\" test."
         We want to skip the inner quotes that are escaped with the \.
         To detect this, we have to look 2 chars behind to find the \.
      */
      if(isquote) {
        char_t *s;
        if(*(s = ptr(bp, cp-2))  == '\\')
          skip = TRUE;
      }
      cp--;
      if(newlines > lun && isrender)
        break;
    }
    if(cp >= 0) {
      if(*ptr(bp, cp) == z) {
//        if(cp < bp->b_page && !isrender)
//          bp->b_reframe = 1;
        return cp;
      }
    }
  } else {
    cp++;
    while ((*(p = ptr(bp, cp)) != z  || depth > 0 || skip || instring) && p <= bp->b_ebuf) {
      if(*p == '\n')
        newlines++;
      /* Skip over stuff like '}' or '(' */
      if(!skip && is_quote(bp, *p)) {
        if(instring) {
          if(*p == *tp)
            instring = !instring;
        } else {
          int ii = 1+cp, found = FALSE;
          point_t ep = pos(bp, bp->b_ebuf);
          tp = ptr(bp, ii);
          for(; ((bp->b_mode != NULL && !bp->b_mode->bmls) || *tp != '\n') && ii <= ep; ii++) {
            tp = ptr(bp, ii);
            if(*tp == *p) {
              found = TRUE;
              break;
            }
            if(*tp == '\\')
              ii++;
          }
          if(found)
            instring = !instring;
        }
      }
      if(*p == op && !skip && !instring) {
        depth++;
      } else if(*p == z && !instring) {
        depth--;
      }
      skip = FALSE;
      if(*p == '\\' && isquote)
        skip = TRUE;
      cp++;
      if(newlines > lun && isrender)
        break;
    }
    if(p < bp->b_ebuf) {
      if(*ptr(bp, cp) == z) {
        if(cp > bp->b_epage && !isrender)
          bp->b_reframe = 1;
        return cp;
      }
    }
  }
  return -1;
}

const char unctrl(char_t p)
{
  return p + (char)64;
}

point_t shift_pmark(int dir, point_t opoint)
{
  point_t mark = curbp->b_pmark[0];
  if(dir) {
    if(mark == opoint) {
      return NOMARK;
    }
    for(int i = PMARK_SIZE-1; i > 0; i--) {
      curbp->b_pmark[i] = curbp->b_pmark[i-1];
    }
    curbp->b_pmark[0] = opoint != NOMARK ? opoint : curbp->b_point;
    return NOMARK;
  }
  for(int i = 0; i < PMARK_SIZE ; i++) {
    if(i+1 > PMARK_SIZE-1)
      curbp->b_pmark[i] = NOMARK;
    else
      curbp->b_pmark[i] = curbp->b_pmark[i+1];
  }
  return mark;
}

int is_combining_unicode(uint32_t result)
{
  struct combine_t range;
  int match = FALSE;
  for(int b = 0; b < RANGES_MAX; b++) {
    range = unicode_combine_ranges[b];
    if(range.end == 0) {
      if(range.start == result) {
        match = TRUE;
        break;
      }
    } else if(range.start <= result && range.end >= result) {
      match = TRUE;
      break;
    }
  }
  return match;
}

static const unsigned char utf8_mask[6] = {0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01};

uint32_t char_to_unicode(char_t *p, int nch)
{
  /* The following 12 lines come from mle(1) */
  char_t mask = utf8_mask[nch - 1];
  uint32_t result = p[0] & mask;
  int i;
  for (i = 1; i < nch /*&& (p + i) < stop*/; ++i) {
    result <<= 6;
    result |= p[i] & 0x3f;
  }
   // replace incomplete code point with replacement char
  if (i != nch) {
    result = 0xfffd;
    nch = i;
  }
  return result;
}

void adjust_bline()
{
  if(curbp->b_opoint > curbp->b_point) {
    curbp->b_opoint--;
    while(curbp->b_opoint >= curbp->b_point) {
      if(*ptr(curbp, curbp->b_opoint) == '\n')
        curbp->b_line--;
      curbp->b_opoint--;
    }
  } else if(curbp->b_opoint < curbp->b_point) {
    while(curbp->b_opoint < curbp->b_point) {
      if(*ptr(curbp, curbp->b_opoint) == '\n')
        curbp->b_line++;
      curbp->b_opoint++;
    }
  }
}
